//
//  BLEManager.m
//  KiWiPeniOSProject
//
//  Created by 曾威林 on 2017/8/27.
//  Copyright © 2017年 zjy. All rights reserved.
//

#import "BLEManager.h"
#import <UIKit/UIKit.h>
#import "KiwipenProtocol.h"
#import "ToolMethods.h"
#import "KiwipenEvent.h"

static BLEManager *shareObject;


#define SERVICE_UUID @"FFE6"//服务
#define CHARACTERISTIC_UUID_READ_INDICATE @"2AF1"//读 订阅
#define CHARACTERISTIC_UUID_WRITE @"2AF2" //写
#define BLUETOOTH_NAME @"KiwiPen"//蓝牙名称
 

/*
#define SERVICE_UUID @"CCC1"//服务
#define CHARACTERISTIC_UUID_READ_INDICATE @"DDD0"//读 订阅
#define CHARACTERISTIC_UUID_WRITE @"DDD2" //写
#define BLUETOOTH_NAME @"iPhone"//蓝牙名称
*/


#define WAIT_POWER_TIMEOUT 9999 //等待开启蓝牙超时时间
#define SCAN_TIMEOUT 9999 //搜索超时时间
#define CONNECT_TIMEOUT 5 //连接超时时间
#define UPDATE_RSSI_INTERVAL 1  //查询信号的定时器间隔时间

#define RSSI_THRESHOLD -40 //信号最小值

@interface BLEManager()<CBCentralManagerDelegate,CBPeripheralDelegate>

@property (nonatomic, copy) BLESearchBlock bleSearchBlock;
@property (nonatomic, copy) BLEConnectBlock bleConnectBlock;

@property (strong, nonatomic) CBCentralManager *centralManager;
@property (strong, nonatomic) CBPeripheral *connectedPeripheral;
@property (copy, nonatomic) NSString *connectedUUIDStr;

@property (strong, nonatomic) CBCharacteristic *readAndIndicateCharacteristic;
@property (strong, nonatomic) CBCharacteristic *writeCharacteristic;

@property (strong, nonatomic)NSMutableArray<CBPeripheral *> *discoverPeripheralList;

@property (strong, nonatomic) NSMutableData *dataBuffer;
@property (nonatomic, assign) NSInteger counter;
@property (nonatomic, assign) SearchType bleSearchType;

@property (nonatomic, strong) NSTimer *updateRSSITimer;

//disPatchAfter
@property (nonatomic, copy) dispatch_block_t connectAfterBlock;

@end

@implementation BLEManager

//创建一个单例
+ (instancetype)shareBLEManager {
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        shareObject = [[BLEManager alloc] init];
        NSDictionary<NSString *, id> *options = @{CBCentralManagerOptionShowPowerAlertKey: @(NO)};
        shareObject.centralManager = [[CBCentralManager alloc]initWithDelegate:shareObject queue:dispatch_get_main_queue() options:options];
    });
    return shareObject;
}

#pragma mark - 公有方法

- (NSString *) getConnectingPeripheralUUID {
    
    if (self.connectedPeripheral.state == CBPeripheralStateConnected) {
        return self.connectedPeripheral.identifier.UUIDString;
    } else {
        return nil;
    }
}

//获取已经连接过的设备
- (void)getMatchedPeripheralsWithSearchBlock:(BLESearchBlock)bleSearchBlock {
    
    if (self.centralManager.state != CBManagerStatePoweredOn) {
        
        if (self.counter <= WAIT_POWER_TIMEOUT) {
            NSLog(@"蓝牙功能未打开，等待1s...");
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                self.counter++;
                [self getMatchedPeripheralsWithSearchBlock:bleSearchBlock];
            });
            
        } else {
            NSLog(@"等待蓝牙开启超时");
            self.counter = 0;
            if (bleSearchBlock) {
                bleSearchBlock(false, nil);
            }
        }
    } else {
        NSLog(@"蓝牙已经开启");
        self.counter = 0;
        [self.discoverPeripheralList removeAllObjects];
        self.bleSearchBlock = bleSearchBlock;
        
        NSMutableArray<NSUUID *> * uuidMutableArray = [NSMutableArray array];
        NSMutableArray<CBUUID *> * cbUuidMutableArray = [NSMutableArray array];
        

        
        
        NSMutableArray<CBPeripheral *> * peripheralArray = [NSMutableArray array];
        //获取已经与系统连接上的设备
        [peripheralArray addObjectsFromArray:[self.centralManager retrieveConnectedPeripheralsWithServices:cbUuidMutableArray]];
        //获取已经匹配过的设备
        [peripheralArray addObjectsFromArray:[self.centralManager retrievePeripheralsWithIdentifiers:uuidMutableArray]];

        if (peripheralArray.count > 0) {
            self.bleSearchBlock(YES, peripheralArray);
        } else {
            self.bleSearchBlock(NO, nil);
        }
    }
}

//扫描设备
- (void)scanPeripheralsWithSearchType:(SearchType) searchType SearchBlock:(BLESearchBlock)bleSearchBlock  {
    
    if (self.centralManager.state != CBManagerStatePoweredOn) {
        
        if (self.counter <= WAIT_POWER_TIMEOUT) {
            NSLog(@"蓝牙功能未打开，等待1s...");
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                self.counter++;
                [self scanPeripheralsWithSearchType:searchType SearchBlock:bleSearchBlock];
            });
            
        } else {
            self.counter = 0;
            NSLog(@"等待蓝牙开启超时");
            if (bleSearchBlock) {
                bleSearchBlock(false, nil);
            }
        }
    } else {
        
        self.counter = 0;
        [self.discoverPeripheralList removeAllObjects];
        self.bleSearchBlock = bleSearchBlock;
        self.bleSearchType = searchType;
        //限制只能搜索出指定UUid的设备
        CBUUID *serviceUUID = [CBUUID UUIDWithString:SERVICE_UUID];
        NSArray<CBUUID *> *uuidList = [NSArray arrayWithObject:serviceUUID];
        
        //调用系统方法
        [self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
        
        //开启倒计时获取搜索结果
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(SCAN_TIMEOUT * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            //两秒后关闭扫描
            [self.centralManager stopScan];
            
            if (self.discoverPeripheralList.count > 0) {
                self.bleSearchBlock(YES, self.discoverPeripheralList);
            } else {
                NSLog(@"超时时间过了，没有搜索到符合要求的设备");
                self.bleSearchBlock(NO, nil);
            }
        });
    }
}

//连接设备
- (void)connectWithPeripheral:(CBPeripheral *)peripheral AndConnectBlock:(BLEConnectBlock)bleConnectBlock {
    self.bleConnectBlock = bleConnectBlock;
    //调用系统方法
    [self.centralManager connectPeripheral:peripheral options:nil];
    
    self.connectAfterBlock = dispatch_block_create(DISPATCH_BLOCK_BARRIER, ^{
        if (self.connectedPeripheral == peripheral && self.connectedPeripheral.state == CBPeripheralStateConnected) {
            [self bleConnectedWithStatus:YES];
        } else {
            
            [self bleConnectedWithStatus:NO];
        }
    });
    
    //两秒后判断是否连接成功
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(CONNECT_TIMEOUT * NSEC_PER_SEC)), dispatch_get_main_queue(), self.connectAfterBlock);
    
}

//发送数据给蓝牙设备
- (void)writeDataWithData:(NSData *)data {
    
    //判断蓝牙是否已经连接
    if (!self.connectedPeripheral || self.connectedPeripheral.state != CBPeripheralStateConnected || !self.readAndIndicateCharacteristic) {
        NSLog(@"发送失败，蓝牙未连接");
        return;
    }
    
    //没有回复消息的类型
    NSLog(@"需要发送的数据是：%@",data);
    
    NSInteger dataLength = data.length;
    
    //一共需要发送的次数
    NSInteger times = dataLength/20;
    if (dataLength % 20 != 0) {
        times++;
    }
    NSLog(@"本次数据需要分%ld次发送", (long)times);
    
    for (int i = 1; i <= times; i++) {
        NSInteger currentLength = 20;
        if (i == times) {
            currentLength = dataLength % 20;
        }
        
        NSData *currentData = [data subdataWithRange:NSMakeRange(20 * (i-1), currentLength)];
        
        NSLog(@"第%ld次发送数据,数据长度是%ld，内容是：%@",(long)(i), (long)currentLength, currentData);
        
        [self.connectedPeripheral writeValue:currentData forCharacteristic:self.writeCharacteristic type:CBCharacteristicWriteWithResponse];
        
        NSLog(@"发送特征UUID:%@",self.writeCharacteristic.UUID.UUIDString);
    }
    
    NSLog(@"数据发送完成");
}

//主动从蓝牙设备读取数据
- (void)readData {
    
    //判断蓝牙是否已经连接
    if (!self.connectedPeripheral || self.connectedPeripheral.state != CBPeripheralStateDisconnected || !self.readAndIndicateCharacteristic) {
        NSLog(@"读取失败，蓝牙未连接");
        return;
    }
    
    [self.connectedPeripheral readValueForCharacteristic:self.readAndIndicateCharacteristic];
}

//取消扫描，断开连接
- (void)cancelAll {
    
    [self.updateRSSITimer invalidate];
    self.updateRSSITimer = nil;
    
    NSLog(@"主动断开连接");
    
    //取消延时任务
    if (self.connectAfterBlock) {
        dispatch_cancel(self.connectAfterBlock);
    }
    //防止重新连接
    self.connectedUUIDStr = nil;
    if (self.connectedPeripheral) {
        [self.centralManager cancelPeripheralConnection:self.connectedPeripheral];
    }
    if (self.centralManager) {
        [self.centralManager stopScan];
    }
    self.readAndIndicateCharacteristic = nil;
    self.writeCharacteristic = nil;
    self.connectedPeripheral = nil;
}




#pragma mark - 懒加载
//懒加载,创建centralmanager
- (CBCentralManager *)centralManager {
    if (!_centralManager) {
        NSDictionary<NSString *, id> *options = @{CBCentralManagerOptionShowPowerAlertKey: @(NO)};
        _centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue() options:options];
    }
    return _centralManager;
}

//懒加载，初始化discoverperipherallist
- (NSMutableArray<CBPeripheral *> *)discoverPeripheralList {
    if (!_discoverPeripheralList) {
        _discoverPeripheralList = [NSMutableArray array];
    }
    return _discoverPeripheralList;
}

#pragma mark - 以下都是cbcentralmanager代理方法

//中心设备状态更新后调用CBCentralManagerStatePoweredOff,CBCentralManagerStatePoweredOn
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    if (central.state == CBCentralManagerStatePoweredOff) {
        NSLog(@"蓝牙已经关闭");
        //请求蓝牙权限
        [self requestUserBlueToothAuth];
        
    }else if(central.state == CBCentralManagerStatePoweredOn) {
        NSLog(@"蓝牙已经打开");
        
    }
}

//进入后台前调用
- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *, id> *)dict {
    
}

//发现外设时调用
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI {
    //NSLog(@"发现蓝牙设备:%@, UUID:%@",peripheral.name, peripheral.identifier.UUIDString);
    //NSLog(@"打印广播信息：%@", advertisementData);

    //通过蓝牙名字过滤
    if (![peripheral.name isEqualToString:BLUETOOTH_NAME]) {
        NSLog(@"蓝牙设备名字不是要找的:%@, 过滤掉",peripheral.name);
        return;
    }
    
    if ([RSSI integerValue] < RSSI_THRESHOLD) {
        NSLog(@"蓝牙设备的信号强度为%ld, 未达到标准%ld！！！", (long)[RSSI integerValue], (long) RSSI_THRESHOLD);
        return;
    }
    
    
    NSLog(@"找到了需要的蓝牙设备！！！");
    //为了不重复添加到数组中的判断
    bool hasReplace = NO;
    for (int i = 0; i < self.discoverPeripheralList.count; i++) {
        
        if (self.discoverPeripheralList[i].identifier.UUIDString == peripheral.identifier.UUIDString) {
            //uuid是识别的唯一标志，同一设备可创建多个对象,做内容替换
            hasReplace = YES;
            self.discoverPeripheralList[i] = peripheral;
        }
    }
    if (!hasReplace) {
        [self.discoverPeripheralList addObject:peripheral];
    }
    
    if (self.discoverPeripheralList.count > 0) {
        self.bleSearchBlock(YES, self.discoverPeripheralList);
        [self.centralManager stopScan];
    }
}

//连接上外设时调用
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    
    //取消延时任务
    dispatch_cancel(self.connectAfterBlock);
    
    //要先连接服务，然后连接到characteristic才算连接成功，先保存peripheral
    //发现的时候就可以给peripheral对象赋值代理了
    peripheral.delegate = self;
    self.connectedPeripheral = peripheral;
    self.connectedUUIDStr = peripheral.identifier.UUIDString;
    
    //限制只能搜索出指定UUid的设备
    CBUUID *serviceUUID = [CBUUID UUIDWithString:SERVICE_UUID];
    //连接服务
    [peripheral discoverServices:@[serviceUUID]];
}

//连接外设失败时调用
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error {
    
    dispatch_cancel(self.connectAfterBlock);
    NSLog(@"连接外设失败%@",error);
    
    [self bleConnectedWithStatus:NO];
}

//掉线和主动断开时调用
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error {
    NSLog(@"连接上以后，设备掉线或主动断开了");
    
    [self bleConnectedWithStatus:NO];
    //重连
    if (self.connectedUUIDStr.length > 0) {//需要重新连接
        NSUUID *uuid = [[NSUUID UUID] initWithUUIDString:self.connectedUUIDStr];
        NSArray *tempArray = [central retrievePeripheralsWithIdentifiers:@[uuid]];
        if (tempArray.count > 0) {
            self.connectedPeripheral = tempArray[0];
            self.connectedPeripheral.delegate = self;
            [central connectPeripheral:self.connectedPeripheral options:nil];
            NSLog(@"尝试重新连接。");
        }
    }
}

#pragma mark - 以下是peripheral代理方法
//peripheral更新名字
- (void)peripheralDidUpdateName:(CBPeripheral *)peripheral {
    
}

//peripheral编辑服务
- (void)peripheral:(CBPeripheral *)peripheral didModifyServices:(NSArray<CBService *> *)invalidatedServices {
    
}

//peripheral更新rssi   这里不知道为什么不运行.....
- (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(nullable NSError *)error {
    if (error) {
        NSLog(@"更新 rssi失败: %@",error);
    } else {
        
    }
}

//peripheral读取到rssi 这里运行
- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error {
    if (error) {
        NSLog(@"获取 rssi失败: %@",error);
    } else {
        NSLog(@"信号强度:%ld",(long)[RSSI integerValue]);
    }
}

//peripheral发现服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error {
    if (error) {
        NSLog(@"discover  Service失败%@",error);
    }else {
        //获取characteristic
        CBUUID *readAndIndicateUUID = [CBUUID UUIDWithString:CHARACTERISTIC_UUID_READ_INDICATE];
        CBUUID *writeUUID = [CBUUID UUIDWithString:CHARACTERISTIC_UUID_WRITE];
        
        for (int i = 0; i < peripheral.services.count; i++) {
            NSLog(@"发现服务：%@",peripheral.services[i].UUID.UUIDString);

            if ([peripheral.services[i].UUID.UUIDString isEqualToString:SERVICE_UUID]) {
                NSLog(@"开始查找特征：");
                [peripheral discoverCharacteristics:@[readAndIndicateUUID,writeUUID] forService:peripheral.services[i]];
            }
        }
    }
}

//peripheral发现指定服务的包含服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverIncludedServicesForService:(CBService *)service error:(nullable NSError *)error {
    
}

//peripheral发现指定服务的characteristics
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error {
    if (error) {
        NSLog(@"发现指定服务的characteristic失败%@",error);
        [self bleConnectedWithStatus:NO];
    }else {
    
        
        for (int i = 0; i < service.characteristics.count; i++) {
            
            NSLog(@"发现指定服务的characteristic成功:%@",service.characteristics[i].UUID.UUIDString);

            if ([service.characteristics[i].UUID.UUIDString isEqualToString:CHARACTERISTIC_UUID_WRITE]) {
                NSLog(@"写特征");
                self.writeCharacteristic = service.characteristics[i];
            }else if ([service.characteristics[i].UUID.UUIDString isEqualToString:CHARACTERISTIC_UUID_READ_INDICATE]) {
                NSLog(@"读 和 订阅");
                self.readAndIndicateCharacteristic = service.characteristics[i];
                //订阅
                [peripheral setNotifyValue:YES forCharacteristic:self.readAndIndicateCharacteristic];
            }
        }
    }
}

//peripheral更新指定characteristic的值
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
    //读取数据成功
    if (error) {
        NSLog(@"读取数据失败%@",error);
    }else {
        NSLog(@"读取数据成功");
        if ([characteristic.UUID.UUIDString isEqualToString:CHARACTERISTIC_UUID_READ_INDICATE]) {
            NSLog(@"收到数据");
            [self dealReceivedData:characteristic.value];
        }
    }
}

//写入指定characteristic的值
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
    if (error) {
        NSLog(@"写入数据失败%@",error);
    }else{
        NSLog(@"写入数据成功");
    }
}

//更新指定characteristic的notification
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
    if (error) {
        NSLog(@"更新notification失败%@",error);
    }else {
        if (characteristic.isNotifying) {
            NSLog(@"订阅成功，uuid:%@", characteristic.UUID.UUIDString);

            [self bleConnectedWithStatus:YES];


        } else {
            NSLog(@"取消订阅，uuid:%@", characteristic.UUID.UUIDString);
        }
    }
}

//发现指定characteristic的描述
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
    
}

//更新指定descriptor的值
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error {
    
}

//写入指定Descriptor的值
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error {
    
}

#pragma mark - 权限申请
//向用户请求蓝牙权限
- (void) requestUserBlueToothAuth {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"打开蓝牙提醒" message:@"使用奇异笔设备需要您打开蓝牙" preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        
        [self openBlueToothSetting];
        
    }]];
    
    //如果用户没有打开蓝牙， 提示用户打开，并且做相应动作
    UIViewController *appRootVC = [UIApplication sharedApplication].keyWindow.rootViewController;
    UIViewController *topVC = appRootVC;
    while (topVC.presentedViewController) {
        topVC = topVC.presentedViewController;
    }
    
    [topVC presentViewController:alert animated:YES completion:^{
    }];
}

//打开蓝牙设置
- (void) openBlueToothSetting {
    
    NSString * urlString = @"App-Prefs:root=Bluetooth";
    if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:urlString]]) {
        
        if ([UIDevice currentDevice].systemVersion.floatValue > 10.0) {
            
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString] options:@{} completionHandler:^(BOOL success) {
                
            }];
            
        } else {
            
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
            
        }
    }
}

//处理接收到的数据，按照头部和尾部进行拼接，得到完整的数据之后再交给block进行处理
- (void) dealReceivedData:(NSData *)data {
    
    //NSLog(@"测试，收到的数据是:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    
    Byte *bytes = (Byte *) data.bytes;
    
    //数据头
    if (bytes[0] == CMD_Begin1 && bytes[1] == CMD_Begin2) {
        self.dataBuffer = [NSMutableData dataWithData:data];
    } else {
        [self.dataBuffer appendData:data];
    }
    
    if (bytes[data.length - 1] == CMD_END) {//数据已经拼接完毕
        NSLog(@"收到完整的数据：%@",self.dataBuffer);
        
        KiwipenEvent *event = [KiwipenEvent CreateEventWithData:self.dataBuffer];
        [event doMethodsJudgingByEventTypeAndEventDetailTypeOfKiwipenEvent];
        
        
        self.dataBuffer = nil;
    }
}

- (void) bleConnectedWithStatus:(Boolean) status {
    //作为事件传递到KiwipenEvent对象进行处理
    KiwipenEvent *event;
    if (status) {
        NSData *data = [self.connectedUUIDStr dataUsingEncoding:NSUTF8StringEncoding];
        event = [KiwipenEvent CreateEventWithType:EventTypeBLEConnected AndData:data];
        
        //开启查询RSSI信号的定时器
        self.updateRSSITimer = [NSTimer scheduledTimerWithTimeInterval:UPDATE_RSSI_INTERVAL target:self selector:@selector(updateRSSI) userInfo:nil repeats:YES];
        
    } else {
        //取消定时器
        [self.updateRSSITimer invalidate];
        self.updateRSSITimer = nil;
        
        event = [KiwipenEvent CreateEventWithType:EventTypeBLEDisConnected AndData:nil];
    }
    [event doMethodsJudgingByEventTypeAndEventDetailTypeOfKiwipenEvent];
    
    //处理回调
    if (self.bleConnectBlock) {
        self.bleConnectBlock(status);
    }
}

//定时查询信号
- (void) updateRSSI {
    [self.connectedPeripheral readRSSI];
}

@end
